package com.backend.utils;

import com.backend.serializer.DateDeserializer;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator.Feature;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.TimeZone;

/**
 * Jackson序列化工具类，必须使用该工具类处理Jackson序列化，不能自己手工new ObjectMapper()
 *
 * @author gaotx
 */
@Slf4j
public class JacksonUtils {

  @SuppressWarnings("staticVariable")
  private static final ObjectMapper OBJECT_MAPPER = newObjectMapper();

  private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

  private static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
  private static final String DATE_PATTERN = "yyyy-MM-dd";
  private static final String TIME_PATTERN = "HH:mm:ss";


  private static ObjectMapper newObjectMapper() {
    // 支持日期反序列化多格式
    SimpleModule simpleModule = new SimpleModule();
    simpleModule.addDeserializer(Date.class, DateDeserializer.instance);
    // 增加对 Java 8 date/time type 的支持
    JavaTimeModule timeModule = new JavaTimeModule();
    timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATETIME_PATTERN)));
    timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_PATTERN)));
    timeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_PATTERN)));
    timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATETIME_PATTERN)));
    timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_PATTERN)));
    timeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_PATTERN)));

    ObjectMapper objectMapper = JsonMapper.builder()
        // 序列化特性
        // 默认日期序列化格式
        .defaultDateFormat(new SimpleDateFormat(DEFAULT_DATE_FORMAT))
        // 默认东八区
        .defaultTimeZone(TimeZone.getTimeZone("GMT+8"))
        // 序列化时忽略值为null的属性
        .serializationInclusion(Include.NON_NULL)
        .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
        .configure(Feature.WRITE_BIGDECIMAL_AS_PLAIN, true)
        // 反序列化特性
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        .configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true)
        // 接受大小写不敏感 默认为false 改为true
        .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
        // 接受空字符串作为空对象，兼容fastjson
        .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
        // 接受单一对象作为数据，兼容fastjson
        .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
        .build();
    objectMapper.registerModules(timeModule, simpleModule);
    return objectMapper;
  }

  /**
   * 获取ObjectMapper实例
   *
   * @return OBJECT_MAPPER
   */
  public static ObjectMapper getInstance() {
    return newObjectMapper();
  }

  public static JsonNode readTree(String json) {
    if (Objects.isNull(json)) {
      return OBJECT_MAPPER.nullNode();
    } else if (StringUtils.isBlank(json)) {
      return OBJECT_MAPPER.missingNode();
    }
    try {
      return OBJECT_MAPPER.readTree(json);
    } catch (JsonProcessingException e) {
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  public static boolean validate(String json) {
    try {
      new ObjectMapper().readTree(json);
    } catch (JsonProcessingException e) {
      return false;
    }
    return true;
  }

  public static <T> T parseObject(InputStream inputStream, Class<T> tClass) {
    Reader reader = new InputStreamReader(inputStream);
    try {
      return OBJECT_MAPPER.readValue(reader, tClass);
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  public static <T> T parseObject(String json, Class<T> tClass) {
    if (StringUtils.isBlank(json)) {
      return null;
    }
    try {
      return OBJECT_MAPPER.readValue(json, tClass);
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  public static <T> T parseObject(JsonNode jsonNode, Class<T> tClass) {
    if (Objects.isNull(jsonNode)) {
      return null;
    }
    try {
      return OBJECT_MAPPER.readValue(OBJECT_MAPPER.writeValueAsString(jsonNode), tClass);
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  public static String toJsonString(Object object) {
    if (Objects.isNull(object)) {
      return null;
    }
    try {
      return OBJECT_MAPPER.writeValueAsString(object);
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  public static byte[] toJSONBytes(Object obj) {
    try {
      if (obj == null) {
        return null;
      }
      return OBJECT_MAPPER.writeValueAsBytes(obj);
    } catch (Throwable e) {
      log.error(e.getMessage(), e);
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  public static <T> List<T> parseList(String json, Class<T> tClass) {
    if (StringUtils.isBlank(json)) {
      return null;
    }
    JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(List.class, tClass);
    try {
      List<T> list = OBJECT_MAPPER.readValue(json, javaType);
      return list;
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  /**
   * @param outClass 外层对象
   * @param inClass  泛型
   */
  public static <T> T parseObject(String json, Class<T> outClass, Class inClass) {
    if (StringUtils.isBlank(json)) {
      return null;
    }
    JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(outClass, inClass);
    try {
      T o = OBJECT_MAPPER.readValue(json, javaType);
      return o;
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  /**
   * 基于TypeReference进行反序列化
   *
   * @param json
   * @param typeReference
   * @param <T>
   * @return
   */
  public static <T> T parseObjectByReference(String json, TypeReference<T> typeReference) {
    if (StringUtils.isBlank(json)) {
      return null;
    }
    try {
      return OBJECT_MAPPER.readValue(json, typeReference);
    } catch (JsonProcessingException e) {
      log.error(e.getMessage(), e);
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  public static <T> T getValue(JsonNode node, String key, Class<T> tClass) {
    if (null == node || node.isNull() || node.get(key) == null || node.get(key).isNull()) {
      return null;
    }
    if (tClass == String.class) {
      return (T) node.get(key).asText();
    }
    if (tClass == Integer.class) {
      return (T) Integer.valueOf(node.get(key).asInt());
    }
    if (tClass == Long.class) {
      return (T) Long.valueOf(node.get(key).asLong());
    }
    if (tClass == Boolean.class) {
      return (T) Boolean.valueOf(node.get(key).asBoolean());
    }

    return null;
  }

  /**
   * 项目上实际使用大多数是从JsonNode里获取字符串内容，新增此方法，方便各个服务使用
   *
   * @param node
   * @param key
   * @return
   */
  public static String getString(JsonNode node, String key) {
    return getValue(node, key, String.class);
  }

  public static <T> T convertValue(Object fromValue, Class<T> toValueType) {
    return OBJECT_MAPPER.convertValue(fromValue, toValueType);
  }

  public static <T> T convertValue(Object fromValue, TypeReference<T> toValueTypeRef) {
    return OBJECT_MAPPER.convertValue(fromValue, toValueTypeRef);
  }

  public static <T> T treeToValue(TreeNode treeNode, Class<T> valueType) {
    try {
      return OBJECT_MAPPER.treeToValue(treeNode, valueType);
    } catch (JsonProcessingException e) {
      log.error(e.getMessage(), e);
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  public static <T> T treeToValue(TreeNode treeNode, TypeReference<T> valueTypeRef) {
    try {
      JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructType(valueTypeRef);
      return OBJECT_MAPPER.treeToValue(treeNode, javaType);
    } catch (JsonProcessingException e) {
      log.error(e.getMessage(), e);
      throw new RuntimeException(e.getMessage(), e);
    }
  }

}
